package svcnode.worker.session;

import java.util.Date;
import java.util.zip.CRC32;
import java.util.Iterator;
import java.util.Map;
//import java.util.LinkedList;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.TreeMap;
import java.util.Random;
import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;

import net.sf.json.JSONObject;
import net.sf.json.JSONArray;

import soar.basic.*;

import svcnode.share.*;

public class Session
{
    private static final String __CLASS__=Session.class.getName();
    private static Session instance=null;

    private Session()
    {/*{{{*/
    }/*}}}*/

    public synchronized static Session getInstance()
    {/*{{{*/
        if(instance == null)
        {
            instance=new Session();
        }
        return instance;
    }/*}}}*/

    public static final int HASH_SIZE=64;
    private static final int MAP_VAL_MAX_SIZE=10*1024;

    private class Sid
    {/*{{{*/
        static final int CREATE_TIME_SIZE=9;
        static final int CONFIRM_SIZE=4;
        static final int UUID_MAX_SIZE=50;
        static final int SID_MIN_SIZE=CREATE_TIME_SIZE+CONFIRM_SIZE+1;
        static final int SID_MAX_SIZE=CREATE_TIME_SIZE+CONFIRM_SIZE+
            UUID_MAX_SIZE;
        static final int SIDENC_MAX_SIZE=SID_MAX_SIZE*2;

        long create_time;
        int confirm;
        String uuid;
    }/*}}}*/
    private class User
    {/*{{{*/
        Sid sid;
        HashMap<String,String> maps;
        long last_access_time;
    }/*}}}*/
    private class Hash
    {/*{{{*/
        TreeMap<String,User> tree;
        //LinkedList<User> list;
    }/*}}}*/
    private Hash[] hashes;
    private Key key;

    public boolean init()
    {/*{{{*/
        this.hashes=new Hash[HASH_SIZE];
        for(int i=0; i<this.hashes.length; ++i)
        {
            this.hashes[i]=new Hash();
            this.hashes[i].tree=new TreeMap<String,User>();
            //this.hashes[i].list=new LinkedList<User>();
        }

        try
        {
            KeyGenerator keyGen=KeyGenerator.getInstance("AES");
            keyGen.init(128);
            this.key=keyGen.generateKey();
        }
        catch(Exception e)
        {
            Errlog.add(__CLASS__+": generate key fail: "+e.toString());
            return false;
        }

        return true;
    }/*}}}*/

    public String call(String svc, JSONObject request)
    {/*{{{*/
        final String __METHOD__="call";

        String response;

        if(svc.equals("CREATE"))
        {
            response=create(request);
        }
        else if(svc.equals("GET"))
        {
            response=get(request);
        }
        else if(svc.equals("VALIDATE"))
        {
            response=validate(request);
        }
        else if(svc.equals("DESTROY"))
        {
            response=destroy(request);
        }
        else if(svc.equals("PUTKEY"))
        {
            response=putkey(request);
        }
        else if(svc.equals("GETKEY"))
        {
            response=getkey(request);
        }
        else if(svc.equals("MODKEY"))
        {
            response=modkey(request);
        }
        else if(svc.equals("DESTROYKEY"))
        {
            response=destroykey(request);
        }
        //else if(svc.equals("ENCRYPT"))
        //{
        //    response=encrypt(request);
        //}
        //else if(svc.equals("DESTROY"))
        //{
        //    response=decrypt(request);
        //}
        else
        {
            Errlog.add("%s.%s: svc(%s) not found",
                    __CLASS__, __METHOD__, svc);
            return "{\"errcode\":\""+SOAR_ERR.SVC_NOT_FOUND+"\"}";
        }
        Stat stat=Stat.getInstance();
        stat.svcCall(svc);

        return response;
    }/*}}}*/

    private String create(JSONObject request)
    {/*{{{*/
        final String __METHOD__="create";
        Cnfg cnfg=Cnfg.getInstance();

        if(!request.has("uuid"))
        {
            Errlog.add("%s.%s: uuid not set", __CLASS__, __METHOD__);
            return "{\"errcode\":\""+SOAR_ERR.PARA+"\"}";
        }
        String uuid=request.getString("uuid");
        if(uuid.isEmpty() || uuid.length() > Sid.UUID_MAX_SIZE)
        {
            Errlog.add("%s.%s: uuid(%s) invalid",
                    __CLASS__, __METHOD__, uuid);
            return "{\"errcode\":\""+SOAR_ERR.PARA+"\"}";
        }

        CRC32 crc32=new CRC32();
        crc32.update(uuid.getBytes());
        int hash_idx=(int)(crc32.getValue()%HASH_SIZE);

        String response=null;

        synchronized(this.hashes[hash_idx])
        {
            Hash hash=this.hashes[hash_idx];
            User user=null;
            try
            {
                user=hash.tree.get(uuid);
            }
            catch(Exception e)
            {
                Errlog.add("%s.%s: get uuid(%s) fail: %s",
                        __CLASS__, __METHOD__, uuid, e.toString());
                return "{\"errcode\":\""+SOAR_ERR.SESSION_CREATE_FAIL+"\"}";
            }
            if(user != null && !user_timeout(hash, user))
            {
                String sid_enc_s=construct_sid(user.sid);
                HashMap<String,String> m=new HashMap<String,String>();
                m.put("errcode", SOAR_ERR.SESSION_EXIST);
                m.put("sid", sid_enc_s);
                response=JSONObject.fromObject(m).toString();
            }
            else
            {
                long now=System.currentTimeMillis()/1000;
                Sid sid=new Sid();
                sid.create_time=now;
                sid.confirm=new Random().nextInt(9999);
                sid.uuid=uuid;
                User newuser=new User();
                newuser.sid=sid;
                newuser.maps=new HashMap<String,String>();
                newuser.last_access_time=now;
                try
                {
                    hash.tree.put(uuid, newuser);
                }
                catch(Exception e)
                {
                    Errlog.add("%s.%s: put uuid(%s) fail: %s",
                            __CLASS__, __METHOD__, uuid, e.toString());
                    response="{\"errcode\":\""+
                        SOAR_ERR.SESSION_CREATE_FAIL+
                        "\"}";
                }

                String sid_enc_s=construct_sid(user.sid);
                HashMap<String,String> m=new HashMap<String,String>();
                m.put("errcode", SOAR_ERR.OK);
                m.put("sid", sid_enc_s);
                response=JSONObject.fromObject(m).toString();
            }
        }

        return response;
    }/*}}}*/

    private String get(JSONObject request)
    {/*{{{*/
        final String __METHOD__="get";
        Cnfg cnfg=Cnfg.getInstance();

        if(!request.has("uuid"))
        {
            Errlog.add("%s.%s: uuid not set", __CLASS__, __METHOD__);
            return "{\"errcode\":\""+SOAR_ERR.PARA+"\"}";
        }
        String uuid=request.getString("uuid");
        if(uuid.isEmpty() || uuid.length() > Sid.UUID_MAX_SIZE)
        {
            Errlog.add("%s.%s: uuid(%s) invalid",
                    __CLASS__, __METHOD__, uuid);
            return "{\"errcode\":\""+SOAR_ERR.PARA+"\"}";
        }

        CRC32 crc32=new CRC32();
        crc32.update(uuid.getBytes());
        int hash_idx=(int)(crc32.getValue()%HASH_SIZE);

        String response=null;

        synchronized(this.hashes[hash_idx])
        {
            Hash hash=this.hashes[hash_idx];
            User user=null;
            try
            {
                user=hash.tree.get(uuid);
            }
            catch(Exception e)
            {
                Errlog.add("%s.%s: get uuid(%s) fail: %s",
                        __CLASS__, __METHOD__, uuid, e.toString());
                return "{\"errcode\":\""+SOAR_ERR.SESSION_NOT_FOUND+"\"}";
            }
            if(user != null && !user_timeout(hash, user))
            {
                String sid_enc_s=construct_sid(user.sid);
                HashMap<String,String> m=new HashMap<String,String>();
                m.put("errcode", SOAR_ERR.OK);
                m.put("sid", sid_enc_s);
                response=JSONObject.fromObject(m).toString();
            }
            else
            {
                Errlog.add("%s.%s: uuid(%s) not found",
                        __CLASS__, __METHOD__, uuid);
                response="{\"errcode\":\""+SOAR_ERR.SESSION_NOT_FOUND+"\"}";
            }
        }

        return response;
    }/*}}}*/

    private String validate(JSONObject request)
    {/*{{{*/
        final String __METHOD__="validate";
        Cnfg cnfg=Cnfg.getInstance();

        if(!request.has("sid"))
        {
            Errlog.add("%s.%s: sid not set", __CLASS__, __METHOD__);
            return "{\"errcode\":\""+SOAR_ERR.PARA+"\"}";
        }
        String sid_s=request.getString("sid");
        if(sid_s.isEmpty() || sid_s.length() > Sid.SIDENC_MAX_SIZE)
        {
            Errlog.add("%s.%s: sid(%s) invalid",
                    __CLASS__, __METHOD__, sid_s);
            return "{\"errcode\":\""+SOAR_ERR.PARA+"\"}";
        }
        Sid sid=parse_sid(sid_s);
        if(sid == null)
        {
            return "{\"errcode\":\""+SOAR_ERR.SESSION_SID_INVALID+"\"}";
        }

        CRC32 crc32=new CRC32();
        crc32.update(sid.uuid.getBytes());
        int hash_idx=(int)(crc32.getValue()%HASH_SIZE);

        String response=null;

        synchronized(this.hashes[hash_idx])
        {
            Hash hash=this.hashes[hash_idx];
            User user=null;
            try
            {
                user=hash.tree.get(sid.uuid);
            }
            catch(Exception e)
            {
                Errlog.add("%s.%s: get uuid(%s) fail: %s",
                        __CLASS__, __METHOD__, sid.uuid, e.toString());
                return "{\"errcode\":\""+SOAR_ERR.SESSION_NOT_FOUND+"\"}";
            }
            if(user != null && !user_timeout(hash, user))
            {
                if(user.sid.create_time != sid.create_time ||
                        user.sid.confirm != sid.confirm)
                {
                    if(cnfg.log.tracking)
                    {
                        Errlog.add("%s.%s: uuid(%s) discord,"+
                                " create_time:(user:%0"+Sid.CREATE_TIME_SIZE+
                                "ld)-(%0"+Sid.CREATE_TIME_SIZE+"ld)"+
                                " confirm:(user:%0"+Sid.CONFIRM_SIZE+
                                "4d)-(%0"+Sid.CONFIRM_SIZE+"d)",
                                __CLASS__, __METHOD__, sid.uuid,
                                user.sid.create_time, sid.create_time,
                                user.sid.confirm, sid.confirm);
                    }
                    response="{\"errcode\":\""+SOAR_ERR.SESSION_SID_DISCORD+
                        "\"}";
                }
                else
                {
                    response="{\"errcode\":\""+SOAR_ERR.OK+"\"}";
                }
            }
            else
            {
                if(cnfg.log.tracking)
                {
                    Errlog.add("%s.%s: uuid(%s) not found",
                            __CLASS__, __METHOD__, sid.uuid);
                }
                response="{\"errcode\":\""+SOAR_ERR.SESSION_NOT_FOUND+"\"}";
            }
        }

        return response;
    }/*}}}*/

    private String destroy(JSONObject request)
    {/*{{{*/
        final String __METHOD__="destroy";
        Cnfg cnfg=Cnfg.getInstance();

        if(!request.has("sid"))
        {
            Errlog.add("%s.%s: sid not set", __CLASS__, __METHOD__);
            return "{\"errcode\":\""+SOAR_ERR.PARA+"\"}";
        }
        String sid_s=request.getString("sid");
        if(sid_s.isEmpty() || sid_s.length() > Sid.SIDENC_MAX_SIZE)
        {
            Errlog.add("%s.%s: sid(%s) invalid",
                    __CLASS__, __METHOD__, sid_s);
            return "{\"errcode\":\""+SOAR_ERR.PARA+"\"}";
        }
        Sid sid=parse_sid(sid_s);
        if(sid == null)
        {
            return "{\"errcode\":\""+SOAR_ERR.SESSION_SID_INVALID+"\"}";
        }

        CRC32 crc32=new CRC32();
        crc32.update(sid.uuid.getBytes());
        int hash_idx=(int)(crc32.getValue()%HASH_SIZE);

        String response=null;

        synchronized(this.hashes[hash_idx])
        {
            Hash hash=this.hashes[hash_idx];
            User user=null;
            try
            {
                user=hash.tree.get(sid.uuid);
            }
            catch(Exception e)
            {
                Errlog.add("%s.%s: get uuid(%s) fail: %s",
                        __CLASS__, __METHOD__, sid.uuid, e.toString());
                return "{\"errcode\":\""+SOAR_ERR.SESSION_NOT_FOUND+"\"}";
            }
            if(user != null && !user_timeout(hash, user))
            {
                if(user.sid.create_time != sid.create_time ||
                        user.sid.confirm != sid.confirm)
                {
                    if(cnfg.log.tracking)
                    {
                        Errlog.add("%s.%s: uuid(%s) discord,"+
                                " create_time:(user:%0"+Sid.CREATE_TIME_SIZE+
                                "ld)-(%0"+Sid.CREATE_TIME_SIZE+"ld)"+
                                " confirm:(user:%0"+Sid.CONFIRM_SIZE+
                                "4d)-(%0"+Sid.CONFIRM_SIZE+"d)",
                                __CLASS__, __METHOD__, sid.uuid,
                                user.sid.create_time, sid.create_time,
                                user.sid.confirm, sid.confirm);
                    }
                    response="{\"errcode\":\""+SOAR_ERR.SESSION_SID_DISCORD+
                        "\"}";
                }
                else
                {
                    hash.tree.remove(sid.uuid);
                    if(cnfg.log.tracking)
                    {
                        Errlog.add("%s.%s: uuid(%s) destroyed",
                                __CLASS__, __METHOD__, sid.uuid);
                    }
                    response="{\"errcode\":\""+SOAR_ERR.OK+"\"}";
                }
            }
            else
            {
                if(cnfg.log.tracking)
                {
                    Errlog.add("%s.%s: uuid(%s) not found",
                            __CLASS__, __METHOD__, sid.uuid);
                }
                response="{\"errcode\":\""+SOAR_ERR.SESSION_NOT_FOUND+"\"}";
            }
        }

        return response;
    }/*}}}*/

    private String putkey(JSONObject request)
    {/*{{{*/
        final String __METHOD__="putkey";
        Cnfg cnfg=Cnfg.getInstance();

        if(!request.has("sid"))
        {
            Errlog.add("%s.%s: sid not set", __CLASS__, __METHOD__);
            return "{\"errcode\":\""+SOAR_ERR.PARA+"\"}";
        }
        String sid_s=request.getString("sid");
        if(sid_s.isEmpty() || sid_s.length() > Sid.SIDENC_MAX_SIZE)
        {
            Errlog.add("%s.%s: sid(%s) invalid",
                    __CLASS__, __METHOD__, sid_s);
            return "{\"errcode\":\""+SOAR_ERR.PARA+"\"}";
        }
        Sid sid=parse_sid(sid_s);
        if(sid == null)
        {
            return "{\"errcode\":\""+SOAR_ERR.SESSION_SID_INVALID+"\"}";
        }
        if(!request.has("keys"))
        {
            Errlog.add("%s.%s: keys not set", __CLASS__, __METHOD__);
            return "{\"errcode\":\""+SOAR_ERR.PARA+"\"}";
        }
        JSONArray keys_a=request.getJSONArray("keys");
        if(keys_a.isEmpty())
        {
            Errlog.add("%s.%s: keys empty", __CLASS__, __METHOD__);
            return "{\"errcode\":\""+SOAR_ERR.OK+"\"}";
        }
        HashMap<String,String> keys=new HashMap<String,String>();
        for(int i=0; i<keys_a.size(); ++i)
        {
            JSONArray pair=keys_a.getJSONArray(i);
            String k=pair.getString(0).trim();
            if(k.isEmpty())
            {
                Errlog.add("%s.%s: key(%s) empty",
                        __CLASS__, __METHOD__, k);
                return "{\"errcode\":\""+SOAR_ERR.PARA+"\"}";
            }
            String v=pair.getString(1).trim();
            if(v.length() > MAP_VAL_MAX_SIZE)
            {
                Errlog.add("%s.%s: val size(%d) > %d",
                        __CLASS__, __METHOD__,
                        v.length(), MAP_VAL_MAX_SIZE);
                return "{\"errcode\":\""+SOAR_ERR.PARA+"\"}";
            }
            keys.put(k, v);
        }

        CRC32 crc32=new CRC32();
        crc32.update(sid.uuid.getBytes());
        int hash_idx=(int)(crc32.getValue()%HASH_SIZE);

        String response=null;

        synchronized(this.hashes[hash_idx])
        {
            Hash hash=this.hashes[hash_idx];
            User user=null;
            try
            {
                user=hash.tree.get(sid.uuid);
            }
            catch(Exception e)
            {
                Errlog.add("%s.%s: get uuid(%s) fail: %s",
                        __CLASS__, __METHOD__, sid.uuid, e.toString());
                return "{\"errcode\":\""+SOAR_ERR.SESSION_NOT_FOUND+"\"}";
            }
            if(user != null && !user_timeout(hash, user))
            {
                if(user.sid.create_time != sid.create_time ||
                        user.sid.confirm != sid.confirm)
                {
                    if(cnfg.log.tracking)
                    {
                        Errlog.add("%s.%s: uuid(%s) discord,"+
                                " create_time:(user:%0"+
                                Sid.CREATE_TIME_SIZE+
                                "ld)-(%0"+Sid.CREATE_TIME_SIZE+"ld)"+
                                " confirm:(user:%0"+Sid.CONFIRM_SIZE+
                                "d)-(%0"+Sid.CONFIRM_SIZE+"d)",
                                __CLASS__, __METHOD__, sid.uuid,
                                user.sid.create_time, sid.create_time,
                                user.sid.confirm, sid.confirm);
                    }
                    response="{\"errcode\":\""+
                        SOAR_ERR.SESSION_SID_DISCORD+"\"}";
                }
                else
                {
                    user.maps.putAll(keys);
                    response="{\"errcode\":\""+SOAR_ERR.OK+"\"}";
                }
            }
            else
            {
                if(cnfg.log.tracking)
                {
                    Errlog.add("%s.%s: uuid(%s) not found",
                            __CLASS__, __METHOD__, sid.uuid);
                }
                response="{\"errcode\":\""+SOAR_ERR.SESSION_NOT_FOUND+"\"}";
            }
        }

        return response;
    }/*}}}*/

    private String getkey(JSONObject request)
    {/*{{{*/
        final String __METHOD__="getkey";
        Cnfg cnfg=Cnfg.getInstance();

        if(!request.has("sid"))
        {
            Errlog.add("%s.%s: sid not set", __CLASS__, __METHOD__);
            return "{\"errcode\":\""+SOAR_ERR.PARA+"\"}";
        }
        String sid_s=request.getString("sid");
        if(sid_s.isEmpty() || sid_s.length() > Sid.SIDENC_MAX_SIZE)
        {
            Errlog.add("%s.%s: sid(%s) invalid",
                    __CLASS__, __METHOD__, sid_s);
            return "{\"errcode\":\""+SOAR_ERR.PARA+"\"}";
        }
        Sid sid=parse_sid(sid_s);
        if(sid == null)
        {
            return "{\"errcode\":\""+SOAR_ERR.SESSION_SID_INVALID+"\"}";
        }
        if(!request.has("keys"))
        {
            Errlog.add("%s.%s: keys not set", __CLASS__, __METHOD__);
            return "{\"errcode\":\""+SOAR_ERR.PARA+"\"}";
        }
        JSONArray keys_a=request.getJSONArray("keys");
        if(keys_a.isEmpty())
        {
            Errlog.add("%s.%s: keys empty", __CLASS__, __METHOD__);
            return "{\"errcode\":\""+SOAR_ERR.OK+"\"}";
        }
        ArrayList<String> keys=new ArrayList<String>();
        for(int i=0; i<keys_a.size(); ++i)
        {
            String k=keys_a.getString(i).trim();
            if(k.isEmpty())
            {
                Errlog.add("%s.%s: key(%s) empty",
                        __CLASS__, __METHOD__, k);
                return "{\"errcode\":\""+SOAR_ERR.PARA+"\"}";
            }
            keys.add(k);
        }

        CRC32 crc32=new CRC32();
        crc32.update(sid.uuid.getBytes());
        int hash_idx=(int)(crc32.getValue()%HASH_SIZE);

        String response=null;

        synchronized(this.hashes[hash_idx])
        {
            Hash hash=this.hashes[hash_idx];
            User user=null;
            try
            {
                user=hash.tree.get(sid.uuid);
            }
            catch(Exception e)
            {
                Errlog.add("%s.%s: get uuid(%s) fail: %s",
                        __CLASS__, __METHOD__, sid.uuid, e.toString());
                return "{\"errcode\":\""+SOAR_ERR.SESSION_NOT_FOUND+"\"}";
            }
            if(user != null && !user_timeout(hash, user))
            {
                if(user.sid.create_time != sid.create_time ||
                        user.sid.confirm != sid.confirm)
                {
                    Errlog.add("%s.%s: uuid(%s) discord,"+
                            " create_time:(user:%0"+
                            Sid.CREATE_TIME_SIZE+
                            "ld)-(%0"+Sid.CREATE_TIME_SIZE+"ld)"+
                            " confirm:(user:%0"+Sid.CONFIRM_SIZE+
                            "d)-(%0"+Sid.CONFIRM_SIZE+"d)",
                            __CLASS__, __METHOD__, sid.uuid,
                            user.sid.create_time, sid.create_time,
                            user.sid.confirm, sid.confirm);
                    response="{\"errcode\":\""+
                        SOAR_ERR.SESSION_SID_DISCORD+"\"}";
                }
                else
                {
                    ArrayList<ArrayList> ks=new ArrayList<ArrayList>(); 
                    Iterator it=keys.iterator();
                    while(it.hasNext())
                    {
                        String k=(String)it.next();
                        String v=user.maps.get(k);
                        if(v == null) v="";
                        ArrayList<String> p=new ArrayList<String>();
                        p.add(k);
                        p.add(v);
                        ks.add(p);
                    }
                    HashMap r=new HashMap();
                    r.put("errcode", SOAR_ERR.OK);
                    r.put("keys", ks);
                    response=JSONObject.fromObject(r).toString();
                }
            }
            else
            {
                if(cnfg.log.tracking)
                {
                    Errlog.add("%s.%s: uuid(%s) not found",
                            __CLASS__, __METHOD__, sid.uuid);
                }
                response="{\"errcode\":\""+SOAR_ERR.SESSION_NOT_FOUND+"\"}";
            }
        }

        return response;
    }/*}}}*/

    private String modkey(JSONObject request)
    {/*{{{*/
        final String __METHOD__="modkey";
        Cnfg cnfg=Cnfg.getInstance();

        if(!request.has("sid"))
        {
            Errlog.add("%s.%s: sid not set", __CLASS__, __METHOD__);
            return "{\"errcode\":\""+SOAR_ERR.PARA+"\"}";
        }
        String sid_s=request.getString("sid");
        if(sid_s.isEmpty() || sid_s.length() > Sid.SIDENC_MAX_SIZE)
        {
            Errlog.add("%s.%s: sid(%s) invalid",
                    __CLASS__, __METHOD__, sid_s);
            return "{\"errcode\":\""+SOAR_ERR.PARA+"\"}";
        }
        if(!request.has("key") || !request.has("delta"))
        {
            Errlog.add("%s.%s: key or delta not set",
                    __CLASS__, __METHOD__);
            return "{\"errcode\":\""+SOAR_ERR.PARA+"\"}";
        }
        String key=request.getString("key").trim();
        if(key.isEmpty())
        {
            Errlog.add("%s.%s: key empty", __CLASS__, __METHOD__);
            return "{\"errcode\":\""+SOAR_ERR.PARA+"\"}";
        }
        String delta_s=request.getString("delta").trim();
        int delta=Integer.parseInt(delta_s);
        if(delta == 0)
        {
            Errlog.add("%s.%s: delta(%s) == 0",
                    __CLASS__, __METHOD__, delta_s);
            return "{\"errcode\":\""+SOAR_ERR.OK+"\"}";
        }

        Sid sid=parse_sid(sid_s);
        if(sid == null)
        {
            return "{\"errcode\":\""+SOAR_ERR.SESSION_SID_INVALID+"\"}";
        }
        CRC32 crc32=new CRC32();
        crc32.update(sid.uuid.getBytes());
        int hash_idx=(int)(crc32.getValue()%HASH_SIZE);

        String response=null;

        synchronized(this.hashes[hash_idx])
        {
            Hash hash=this.hashes[hash_idx];
            User user=null;
            try
            {
                user=hash.tree.get(sid.uuid);
            }
            catch(Exception e)
            {
                Errlog.add("%s.%s: get uuid(%s) fail: %s",
                        __CLASS__, __METHOD__, sid.uuid, e.toString());
                return "{\"errcode\":\""+SOAR_ERR.SESSION_NOT_FOUND+"\"}";
            }
            if(user != null && !user_timeout(hash, user))
            {
                if(user.sid.create_time != sid.create_time ||
                        user.sid.confirm != sid.confirm)
                {
                    Errlog.add("%s.%s: uuid(%s) discord,"+
                            " create_time:(user:%0"+
                            Sid.CREATE_TIME_SIZE+
                            "ld)-(%0"+Sid.CREATE_TIME_SIZE+"ld)"+
                            " confirm:(user:%0"+Sid.CONFIRM_SIZE+
                            "d)-(%0"+Sid.CONFIRM_SIZE+"d)",
                            __CLASS__, __METHOD__, sid.uuid,
                            user.sid.create_time, sid.create_time,
                            user.sid.confirm, sid.confirm);
                    response="{\"errcode\":\""+
                        SOAR_ERR.SESSION_SID_DISCORD+"\"}";
                }
                else
                {
                    try
                    {
                        String val=user.maps.get(key);
                        if(val == null)
                        {
                            response="{\"errcode\":\""+
                                SOAR_ERR.SESSION_KEY_NOT_FOUND+"\"}";
                        }
                        else
                        {
                            if(val.matches("/^0-9/"))
                            {
                                Errlog.add("%s.%s: val(%s) not int",
                                        __CLASS__, __METHOD__, val);
                                response="{\"errcode\":\""+
                                    SOAR_ERR.SESSION_VAL_NOT_INT+"\"}";
                            }
                            else
                            {
                                int v=Integer.parseInt(val)+delta;
                                val=Integer.toString(v);
                                user.maps.put(key, val);
                                response="{\"errcode\":\""+
                                    SOAR_ERR.OK+"\"}";
                            }
                        }
                    }
                    catch(Exception e)
                    {
                        Errlog.add("%s.%s: uuid(%s) set maps fail: %s",
                                __CLASS__, __METHOD__,
                                sid.uuid, e.toString());
                        response="{\"errcode\":\""+
                            SOAR_ERR.SESSION_KEY_NOT_FOUND+"\"}";
                    }
                }
            }
            else
            {
                if(cnfg.log.tracking)
                {
                    Errlog.add("%s.%s: uuid(%s) not found",
                            __CLASS__, __METHOD__, sid.uuid);
                }
                response="{\"errcode\":\""+
                    SOAR_ERR.SESSION_NOT_FOUND+"\"}";
            }
        }

        return response;
    }/*}}}*/

    private String destroykey(JSONObject request)
    {/*{{{*/
        final String __METHOD__="destroykey";
        Cnfg cnfg=Cnfg.getInstance();

        if(!request.has("sid"))
        {
            Errlog.add("%s.%s: sid not set", __CLASS__, __METHOD__);
            return "{\"errcode\":\""+SOAR_ERR.PARA+"\"}";
        }
        String sid_s=request.getString("sid");
        if(sid_s.isEmpty() || sid_s.length() > Sid.SIDENC_MAX_SIZE)
        {
            Errlog.add("%s.%s: sid(%s) invalid",
                    __CLASS__, __METHOD__, sid_s);
            return "{\"errcode\":\""+SOAR_ERR.PARA+"\"}";
        }
        if(!request.has("key") || !request.has("delta"))
        {
            Errlog.add("%s.%s: key or delta not set",
                    __CLASS__, __METHOD__);
            return "{\"errcode\":\""+SOAR_ERR.PARA+"\"}";
        }
        String key=request.getString("key").trim();
        if(key.isEmpty())
        {
            Errlog.add("%s.%s: key empty", __CLASS__, __METHOD__);
            return "{\"errcode\":\""+SOAR_ERR.PARA+"\"}";
        }

        Sid sid=parse_sid(sid_s);
        if(sid == null)
        {
            return "{\"errcode\":\""+SOAR_ERR.SESSION_SID_INVALID+"\"}";
        }
        CRC32 crc32=new CRC32();
        crc32.update(sid.uuid.getBytes());
        int hash_idx=(int)(crc32.getValue()%HASH_SIZE);

        String response=null;

        synchronized(this.hashes[hash_idx])
        {
            Hash hash=this.hashes[hash_idx];
            User user=null;
            try
            {
                user=hash.tree.get(sid.uuid);
            }
            catch(Exception e)
            {
                Errlog.add("%s.%s: get uuid(%s) fail: %s",
                        __CLASS__, __METHOD__, sid.uuid, e.toString());
                return "{\"errcode\":\""+SOAR_ERR.SESSION_NOT_FOUND+"\"}";
            }
            if(user != null && !user_timeout(hash, user))
            {
                if(user.sid.create_time != sid.create_time ||
                        user.sid.confirm != sid.confirm)
                {
                    Errlog.add("%s.%s: uuid(%s) discord,"+
                            " create_time:(user:%0"+
                            Sid.CREATE_TIME_SIZE+
                            "ld)-(%0"+Sid.CREATE_TIME_SIZE+"ld)"+
                            " confirm:(user:%0"+Sid.CONFIRM_SIZE+
                            "d)-(%0"+Sid.CONFIRM_SIZE+"d)",
                            __CLASS__, __METHOD__, sid.uuid,
                            user.sid.create_time, sid.create_time,
                            user.sid.confirm, sid.confirm);
                    response="{\"errcode\":\""+
                        SOAR_ERR.SESSION_SID_DISCORD+"\"}";
                }
                else
                {
                    try
                    {
                        user.maps.remove(key);
                        response="{\"errcode\":\""+SOAR_ERR.OK+"\"}";
                    }
                    catch(Exception e)
                    {
                        Errlog.add("%s.%s: uuid(%s) destroy key(%s)"+
                               " fail: %s",
                                __CLASS__, __METHOD__,
                                sid.uuid, key, e.toString());
                        return "{\"errcode\":\""+
                            SOAR_ERR.SESSION_KEY_NOT_FOUND+"\"}";
                    }
                }
            }
            else
            {
                if(cnfg.log.tracking)
                {
                    Errlog.add("%s.%s: uuid(%s) not found",
                            __CLASS__, __METHOD__, sid.uuid);
                }
                response="{\"errcode\":\""+
                    SOAR_ERR.SESSION_NOT_FOUND+"\"}";
            }
        }

        return response;
    }/*}}}*/

    //private String encrypt(JSONObject request)
    //{
    //    final String __METHOD__="encrypt";
    //    Cnfg cnfg=Cnfg.getInstance();
    //    String response=null;
    //    return response;
    //}

    //private String decrypt(JSONObject request)
    //{
    //    final String __METHOD__="decrypt";
    //    Cnfg cnfg=Cnfg.getInstance();
    //    String response=null;
    //    return response;
    //}

    public void cleanTimeout(int hash_idx)
    {/*{{{*/
        final String __METHOD__="cleanTimeout";
        Cnfg cnfg=Cnfg.getInstance();

        long now=System.currentTimeMillis()/1000;

        synchronized(this.hashes[hash_idx])
        {
            Hash hash=this.hashes[hash_idx];
            Iterator it=hash.tree.entrySet().iterator();
            while(it.hasNext())
            {
                Map.Entry entry=(Map.Entry)it.next();
                String uuid=(String)entry.getKey();
                User user=(User)entry.getValue();
                if(now - user.last_access_time > cnfg.session.timeout)
                {
                    if(cnfg.log.tracking)
                    {
                        Errlog.add("%s.%s: uuid(%s) TIMEOUT",
                               __CLASS__, __METHOD__, uuid);
                    }
                    it.remove();
                }
            }
        }
    }/*}}}*/

    private String construct_sid(Sid sid)
    {/*{{{*/
        final String __METHOD__="construct_sid";

        String sid_s=String.format("%0"+Sid.CREATE_TIME_SIZE+"ld%0"+
                Sid.CONFIRM_SIZE+"d%s",
                sid.create_time, sid.confirm, sid.uuid);
        String sid_enc_s=null;
        try
        {
            Cipher cipher=Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, this.key);
            sid_enc_s=Misc.ExpandToString(cipher.doFinal(
                        sid_s.getBytes()));
        }
        catch(Exception e)
        {
            Errlog.add("%s.%s: encrypt sid fail: %s",
                    __CLASS__, __METHOD__, e.toString());
            return null;
        }
        return sid_enc_s;
    }/*}}}*/

    private Sid parse_sid(String sid_enc)
    {/*{{{*/
        final String __METHOD__="parse_sid";

        String sid_s=null;
        try
        {
            byte[] sid_enc_b=Misc.CollapseFromString(sid_enc);
            Cipher cipher=Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, this.key);
            sid_s=new String(cipher.doFinal(sid_enc_b));
        }
        catch(Exception e)
        {
            Errlog.add("%s.%s: decrypt sid fail: %s",
                    __CLASS__, __METHOD__, e.toString());
            return null;
        }
        int len=sid_s.length();
        if(len < Sid.SID_MIN_SIZE || len > Sid.SID_MAX_SIZE)
        {
            Errlog.add("%s.%s: plain sid(%s) invalid: ",
                    __CLASS__, __METHOD__, sid_s);
            return null;
        }

        Sid sid=new Sid();
        sid.create_time=Long.parseLong(
                sid_s.substring(0, Sid.CREATE_TIME_SIZE));
        sid.confirm=Integer.parseInt(
                sid_s.substring(Sid.CREATE_TIME_SIZE,
                    Sid.CREATE_TIME_SIZE+Sid.CONFIRM_SIZE));
        sid.uuid=sid_s.substring(
                Sid.CREATE_TIME_SIZE+Sid.CONFIRM_SIZE).trim();
        return sid;
    }/*}}}*/

    private boolean user_timeout(Hash hash, User user)
    {/*{{{*/
        final String __METHOD__="user_timeout";
        Cnfg cnfg=Cnfg.getInstance();

        long now=System.currentTimeMillis()/1000;

        if(now - user.last_access_time > cnfg.session.timeout)
        {
            Errlog.add("%s.%s: uuid(%s) TIMEOUT",
                    __CLASS__, __METHOD__, user.sid.uuid);
            try
            {
                hash.tree.remove(user.sid.uuid);
            }
            catch(Exception e)
            {
                Errlog.add("%s.%s: remove uuid(%s) fail: %s",
                    __CLASS__, __METHOD__, user.sid.uuid, e.toString());
            }
            return false;
        }
        else
        {
            user.last_access_time=now;
            return true;
        }
    }/*}}}*/
}
